package org.osomit.sacct.handler.impl;

import org.osomit.sacct.handler.iface.ClientServiceHandler;
import org.osomit.sacct.entity.*;
import org.osomit.sacct.connector.socket.iface.SocketConnector;
import org.osomit.sacct.builder.ResponseBuilder;
import org.osomit.sacct.security.utils.SecurityUtil;
import org.osomit.sacct.security.utils.SharedSecurityGenerator;
import org.osomit.sacct.session.SecureRequestContext;
import org.osomit.sacct.session.SessionContext;
import org.osomit.sacct.SAcctException;
import org.stringtree.json.JSONReader;

import java.util.List;
import java.util.ArrayList;

/**
 * @author Jian Fang (John.Jian.Fang@gmail.com)
 *
 * Date: Jun 29, 2009
 */
public class ClientServiceHandlerImpl implements ClientServiceHandler {

    private static final String ERROR_CONNECT_TO_SACCT_SERVER = "Error connecting to SAcct Server: ";
    private static final String ERROR_DISCONNECT_FROM_SACCT_SERVER = "Error disconnecting from SAcct Server: ";
    private static final String ERROR_FEATCH_ALL_ACCOUNTS = "Error fetching all accounts from SAcct Server: ";
    private static final String ERROR_FEATCH_ACCOUNT = "Error fetching account from SAcct Server for account ";
    private static final String ERROR_SAY_HELLO_TO_SACCT_SERVER = "Error saying hello to SAcct Server: ";
    
    private JSONReader jsreader = new JSONReader();

    private ResponseBuilder builder = new ResponseBuilder();

    private SocketConnector connector;

    public SocketConnector getConnector() {
        return connector;
    }

    public void connect() {
        this.connector.connect();
    }

    public void disconnect() {
        this.connector.disconnect();
    }

    public void setConnector(SocketConnector connector) {
        this.connector = connector;
    }

    public SessionInfo handshake(RequestContext requestContext, String clientPubKey) {
        Request request = new Request();
        request.setCommand("handshake");
        request.setRnd(SecurityUtil.base64Encode(SharedSecurityGenerator.generateSharedSecret(64)));
        List params = new ArrayList();
        params.add(clientPubKey);
        request.setParameters(params);
        request.setRequestContext(requestContext);
        SecureRequest secureRequest = new SecureRequest();
        secureRequest.setRequest(SecurityUtil.base64Encode(request.toJSON().toString()));
        secureRequest.setSessionId(null);
        SecureResponse response = connector.submit(secureRequest);
        Object obj = jsreader.read(SecurityUtil.base64Decode(response.getResponse()));
        Response resp = builder.build(obj);

        if(resp.isSuccessful()){
            return (SessionInfo) resp.getReturnValue().get(0);
        }

        throw new SAcctException(ERROR_CONNECT_TO_SACCT_SERVER + resp.getErrorCode());
    }

    public String closeSession(RequestContext requestContext, String goodbye) {
        Request request = new Request();
        request.setCommand("closeSession");
        request.setRnd(SecurityUtil.base64Encode(SharedSecurityGenerator.generateSharedSecret(64)));
        List params = new ArrayList();
        params.add(goodbye);
        request.setParameters(params);
        request.setRequestContext(requestContext);
        SecureRequestContext src = (SecureRequestContext)requestContext;
        SessionContext sc = src.getSessionContext();
        SecureRequest secureRequest = new SecureRequest();
        String securedRequest = SecurityUtil.encrypt(sc.getSessionKey(), request.toJSON().toString());
        secureRequest.setRequest(securedRequest);
        secureRequest.setSessionId(sc.getSessionId());
        SecureResponse response = connector.submit(secureRequest);
        String decrypted = SecurityUtil.decrypt(sc.getSessionKey(), response.getResponse());
        Object obj = jsreader.read(decrypted);
        Response resp = builder.build(obj);

        if(resp.isSuccessful()){
            return (String) resp.getReturnValue().get(0);
        }

        throw new SAcctException(ERROR_DISCONNECT_FROM_SACCT_SERVER + resp.getErrorCode());
    }

    public ApplicationAccount fetchAccount(RequestContext requestContext, String accountName) {
        Request request = new Request();
        request.setCommand("fetchAccount");
        request.setRnd(SecurityUtil.base64Encode(SharedSecurityGenerator.generateSharedSecret(64)));
        List params = new ArrayList();
        params.add(accountName);
        request.setParameters(params);
        request.setRequestContext(requestContext);
        SecureRequestContext src = (SecureRequestContext)requestContext;
        SessionContext sc = src.getSessionContext();
        SecureRequest secureRequest = new SecureRequest();
        String securedRequest = SecurityUtil.encrypt(sc.getSessionKey(), request.toJSON().toString());
        secureRequest.setRequest(securedRequest);
        secureRequest.setSessionId(sc.getSessionId());
        SecureResponse response = connector.submit(secureRequest);
        String decrypted = SecurityUtil.decrypt(sc.getSessionKey(), response.getResponse());
        Object obj = jsreader.read(decrypted);
        Response resp = builder.build(obj);

        if(resp.isSuccessful()){
            return (ApplicationAccount) resp.getReturnValue().get(0);
        }

        throw new SAcctException(ERROR_FEATCH_ACCOUNT + accountName + ": " + resp.getErrorCode());
    }

    public List<ApplicationAccount> fetchAllAccounts(RequestContext requestContext) {
        Request request = new Request();
        request.setCommand("fetchAllAccounts");
        request.setRnd(SecurityUtil.base64Encode(SharedSecurityGenerator.generateSharedSecret(64)));
        request.setParameters(null);
        request.setRequestContext(requestContext);
        SecureRequestContext src = (SecureRequestContext)requestContext;
        SessionContext sc = src.getSessionContext();
        SecureRequest secureRequest = new SecureRequest();
        String securedRequest = SecurityUtil.encrypt(sc.getSessionKey(), request.toJSON().toString());
        secureRequest.setRequest(securedRequest);
        secureRequest.setSessionId(sc.getSessionId());
        SecureResponse response = connector.submit(secureRequest);
        String decrypted = SecurityUtil.decrypt(sc.getSessionKey(), response.getResponse());
        Object obj = jsreader.read(decrypted);
        Response resp = builder.build(obj);

        if(resp.isSuccessful()){
            return (List<ApplicationAccount>)resp.getReturnValue();
        }

        throw new SAcctException(ERROR_FEATCH_ALL_ACCOUNTS + resp.getErrorCode());
    }

    public String hello(RequestContext requestContext) {
        Request request = new Request();
        request.setCommand("hello");
        request.setRnd(SecurityUtil.base64Encode(SharedSecurityGenerator.generateSharedSecret(64)));
        request.setParameters(null);
        request.setRequestContext(requestContext);
        SecureRequest secureRequest = new SecureRequest();
        secureRequest.setRequest(SecurityUtil.base64Encode(request.toJSON().toString()));
        secureRequest.setSessionId(null);
        SecureResponse response = connector.submit(secureRequest);
        Object obj = jsreader.read(SecurityUtil.base64Decode(response.getResponse()));
        Response resp = builder.build(obj);
        
        if(resp.isSuccessful()){
            return (String) resp.getReturnValue().get(0);
        }

        throw new SAcctException(ERROR_SAY_HELLO_TO_SACCT_SERVER + resp.getErrorCode());
    }
}
